package logger

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"os"
	"runtime"
	"strconv"
	"strings"
	"sync/atomic"
	"time"

	"github.com/elastic/go-elasticsearch/v9"
	"github.com/elastic/go-elasticsearch/v9/esapi"
)

// Config 日志配置结构体，替代全局flag变量，提升灵活性
type Config struct {
	Mode         int      // 日志模式：1=标准输出，2=ES存储 ,3 : 输出到文件
	MachineIP    string   // 机器IP地址
	LogIndexName string   // ES索引名称
	EsHosts      []string // ES服务地址列表
	EnableCaller bool     // 是否记录文件路径和行号
	ChanSize     int      // 发送通道缓冲大小（控制并发）
	EsWorkerNum  int      // ES处理协程数量
	filename     string
}

var (
	STDOUT    = 1
	ES_STORE  = 2
	FILE_SORE = 3
)

var (
	TARGET = 0
	INFO   = 0
	DEBUG  = 1
	WARN   = 2
	ERROR  = 3
	FATAL  = 4
)

// DefaultConfig 返回默认配置
func DefaultConfig(mode int) Config {
	return Config{
		Mode:         mode,
		MachineIP:    "127.0.0.1",
		LogIndexName: "logs",
		EsHosts:      []string{"http://127.0.0.1:9200"},
		EnableCaller: true, //是否开启文件名和行号
		ChanSize:     10000,
		EsWorkerNum:  5, // 默认5个协程处理ES写入
		filename:     "./log.backup",
	}
}

// LogInfo 日志信息结构体，用于ES存储
type LogInfo struct {
	Level      string `json:"Level"`
	Ip         string `json:"Ip"`
	Pid        string `json:"Pid"`
	FilePath   string `json:"file_path"`
	Line       string `json:"line"`
	CreateTime string `json:"create_time"`
	Body       string `json:"body"`
}

// Logger 日志处理器
type Logger struct {
	conf         Config
	mode         atomic.Int32 // 日志模式（原子操作保证并发安全）
	esClient     *elasticsearch.Client
	syncBuilder  *strings.Builder // 同步模式缓冲区（替代string减少分配)
	pidStr       string           // 缓存PID字符串（进程生命周期不变）
	machineIP    string           // 缓存机器IP
	enableCaller bool             // 是否开启行号记录
	esChan       chan LogInfo     // ES发送通道（控制并发）
	fileChan     chan LogInfo     //文件发送通道
	logIndex     string           // ES索引名称
}

// NewLogger 根据配置创建日志实例
func NewLogger(cfg Config) (*Logger, error) {
	// 初始化ES客户端（如为ES模式）
	var esClient *elasticsearch.Client
	var err error
	if cfg.Mode == 2 {
		esConfig := elasticsearch.Config{Addresses: cfg.EsHosts}
		esClient, err = elasticsearch.NewClient(esConfig)
		if err != nil {
			return nil, fmt.Errorf("创建ES客户端失败: %w", err)
		}
	}

	// 初始化日志实例
	logger := &Logger{
		syncBuilder:  &strings.Builder{},
		pidStr:       strconv.Itoa(os.Getpid()),
		machineIP:    cfg.MachineIP,
		enableCaller: cfg.EnableCaller,
		logIndex:     cfg.LogIndexName,
		esClient:     esClient,
		conf:         cfg,
	}
	logger.mode.Store(int32(cfg.Mode))

	// 初始化ES通道和工作协程（仅ES模式）
	if cfg.Mode == ES_STORE {
		logger.esChan = make(chan LogInfo, cfg.ChanSize)
		for i := 0; i < cfg.EsWorkerNum; i++ {
			go logger.esWorker()
		}
	}
	if cfg.Mode == FILE_SORE {
		logger.fileChan = make(chan LogInfo, cfg.ChanSize)
		go logger.fileWorker()
	}
	return logger, nil
}

func (l *Logger) fileWorker() {
	file, err := os.OpenFile(l.conf.filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return
	}
	defer file.Close()
	for logInfo := range l.fileChan {
		logJSON, err := json.Marshal(logInfo)
		if err != nil {
			continue
		}
		if _, err := file.WriteString(string(logJSON)); err != nil {
			file.WriteString(string(logJSON))
		}
	}
}

// esWorker ES日志处理工作协程（从通道取任务处理）
func (l *Logger) esWorker() {
	for logInfo := range l.esChan {
		// 序列化日志信息
		logJSON, err := json.Marshal(logInfo)
		if err != nil {
			fmt.Fprintf(os.Stdout, "日志JSON序列化失败: %v\n", err)
			continue
		}

		// 发送到ES
		req := esapi.IndexRequest{
			Index:      l.logIndex,
			DocumentID: "",
			Body:       bytes.NewReader(logJSON), // 直接用[]byte避免字符串转换
			Refresh:    "false",
		}

		res, err := req.Do(context.Background(), l.esClient)
		if err != nil || res.IsError() {
			continue
		}
		res.Body.Close()
	}
}

// getCurrentNameAndLineWithSkip 获取调用者文件路径和行号（支持动态调整调用栈深度）
func getCurrentNameAndLineWithSkip(skip int) (filepath string, line int) {
	_, file, line, ok := runtime.Caller(skip)
	if !ok {
		return "unknown", 0
	}
	return file, line
}

// getNowTimeString 获取格式化的当前时间
func getNowTimeString() string {
	return time.Now().Format("2006/01/02-15:04:05")
}

// addFmtToSyncBuff 构建同步模式日志字符串（使用strings.Builder减少分配）
func (l *Logger) addFmtToSyncBuff(targets []string) {
	l.syncBuilder.Reset() // 复用缓冲区
	for _, target := range targets {
		l.syncBuilder.WriteString("[")
		l.syncBuilder.WriteString(target)
		l.syncBuilder.WriteString("]")
	}
}

// logToStdout 输出到标准输出
func (l *Logger) logToStdout() {
	fmt.Println(l.syncBuilder.String())
}

// createLogInfo 创建ES模式日志信息
func (l *Logger) createLogInfo(level, body, path string, line int) LogInfo {
	return LogInfo{
		Level:      "[" + level + "]",
		Ip:         "[" + l.machineIP + "]",
		Pid:        "[" + l.pidStr + "]",
		FilePath:   "[" + path + "]",
		Line:       "[" + strconv.Itoa(line) + "]",
		CreateTime: "[" + getNowTimeString() + "]",
		Body:       "[" + body + "]",
	}
}

// backUp 发送到ES（通过通道限制并发）
func (l *Logger) backUp(logInfo *LogInfo) {
	select {
	case l.esChan <- (*logInfo):
		// 成功放入通道
	default:
		// 通道满时降级处理
		fmt.Fprintf(os.Stderr, "ES通道已满,日志丢失: %+v\n", logInfo)
	}
}

func (l *Logger) toFile(info *LogInfo) {
	select {
	case l.fileChan <- (*info):
		// 成功放入通道
	default:
		// 通道满时不处理--TODO 或者降级
		return
	}
}

// log 通用日志处理方法（抽象公共逻辑）
func (l *Logger) log(level string, format string, args ...any) {
	// 格式化日志内容
	body := fmt.Sprintf(format, args...)

	// 获取文件路径和行号（按需）
	var path string = "unknown"
	var line int = 0
	if l.enableCaller {
		// 调用栈深度+1（因为多了一层log方法）
		path, line = getCurrentNameAndLineWithSkip(3)
	}

	// 根据模式处理日志
	switch l.mode.Load() {
	case int32(STDOUT): // 标准输出模式
		l.addFmtToSyncBuff([]string{
			level,
			l.machineIP,
			l.pidStr,
			path,
			strconv.Itoa(line),
			getNowTimeString(),
			body,
		})
		l.logToStdout()
	case int32(ES_STORE): // ES模式
		logInfo := l.createLogInfo(level, body, path, line)
		l.backUp(&logInfo)
	case int32(FILE_SORE):
		logInfo := l.createLogInfo(level, body, path, line)
		l.toFile(&logInfo)
	}
}
func SetLogOutPutLevel(level int) {
	TARGET = level
}

// 以下为各级别日志方法（复用通用log方法）
func (l *Logger) DEBUG(format string, args ...any) {
	if TARGET < DEBUG {
		return
	}
	l.log("DEBUG", format, args...)
}

func (l *Logger) INFO(format string, args ...any) {
	if TARGET < INFO {
		return
	}
	l.log("INFO", format, args...)
}

func (l *Logger) WARN(format string, args ...any) {
	if TARGET < WARN {
		return
	}
	l.log("WARN", format, args...)
}

func (l *Logger) ERROR(format string, args ...any) {
	if TARGET < ERROR {
		return
	}
	l.log("ERROR", format, args...)
}

func (l *Logger) FATAL(format string, args ...any) {
	if TARGET < FATAL {
		return
	}
	l.log("FATAL", format, args...)
}

// 从命令行参数加载配置（辅助函数）TODO
func LoadConfigFromFlags(mode int, es_hosts ...string) Config {
	cfg := DefaultConfig(mode)
	cfg.EsHosts = es_hosts
	return cfg
}
